import json
import os
from not_so_free_marker import proc_loop, proc_var, proc_include, file_helper, proc_if, proc_comment
from not_so_free_marker.enums import FileOpResultEnum, MarkTypeEnum


# 处理文本
# template_text:要处理的模板文本
# data_map:用于 填充模板的数据,map格式
# include_folder_path:如果用到include指令,需要指定父文件夹路径,如果不指定,在本文件夹下寻找
def process(template_text: str, data_map: map, include_folder_path=""):
    # 去掉注释
    template_text = proc_comment.process(template_text)
    # 处理引用(include)
    template_text = proc_include.process(include_folder_path,
                                         template_text)  # 仅仅支持引入一级文件,感觉通常,引入一级就够用了.如果要实现任意多级处理,改为迭代即可.
    # 去掉引入文件的注释
    template_text = proc_comment.process(template_text)

    # file_helper.writeFile(r"e:\eee\file_result_1.txt", template_text)  # !!!!!!!!!!!!!!!!!!!!
    # 检查模板
    allTags = __private_check_template(template_text)
    # 处理循环
    looped_text = proc_loop.process(template_text, data_map)

    # file_helper.writeFile(r"e:\eee\file_result_2.txt", looped_text)  # !!!!!!!!!!!!!!!!!!!!

    # 转化变量标记,执行表达式
    vared_text = proc_var.process(looped_text, data_map)

    # file_helper.writeFile(r"e:\eee\file_result_3.txt", vared_text)  # !!!!!!!!!!!!!!!!!!!!

    # 条件表达式
    ifed_text = proc_if.process(vared_text)

    # file_helper.writeFile(r"e:\eee\file_result_4.txt", ifed_text)  # !!!!!!!!!!!!!!!!!!!!

    return ifed_text


# 处理文件
# template_text:模板文件的路径
# data_map:数据文件的路径
def process_file(template_text_path: str, data_json_path: str):
    # 读取文件
    template_text, data_json = __private_read_files(data_json_path, template_text_path)
    template_folder_path = os.path.dirname(template_text_path)
    # 读取json数据
    data_map = __private_preprocess_json(data_json)
    return process(template_text, data_map, template_folder_path)


def __private_read_files(data_json_path, template_text_path):
    template_result, template_content = file_helper.read_file(template_text_path)
    if template_result != FileOpResultEnum.OK:
        raise Exception(template_result)

    json_result, json_content = file_helper.read_file(data_json_path)
    if json_result != FileOpResultEnum.OK:
        raise Exception(json_result)
    return template_content, json_content


def __private_preprocess_json(data_json):
    # 把data_json预处理一下,将{a:value}变成{"a":value},否则json组件识别不出来.
    # data_json = re.sub(r"(\w+)\s*:", lambda m: "\"{0}\":".format(m.group(1)), data_json)
    data_map = json.loads(data_json)
    if isinstance(data_map, list):
        raise Exception("数据不能是列表类型")
    return data_map


# 预先检查模板的合法性
def __private_check_template(template_text: str):
    # 检查循环和if标记
    # 规则:1.for必须配对,2.if和#if中间才可以有elseif和else,3.if和for标记不能交叉.
    # 方法:用栈.并检查栈顶元素.
    all_tags = []
    all_tags.extend(proc_if.get_if_tags(template_text))
    all_tags.extend(proc_loop.get_loop_tags(template_text))
    all_tags.sort(key=lambda a: a.position)

    __private_check_tags(all_tags)
    # 检查变量,利用栈
    proc_var.check_var_tags(template_text)

    return all_tags


# 利用栈做标签匹配.顺序遍历所有标签,遇到开始标签,入栈,遇到结束标签,看和栈顶是否成对,不成对就说明标签配对不正确.
def __private_check_tags(tags: list):
    tag_check_stack = []
    for tag in tags:
        if tag.type in (MarkTypeEnum.REG_LIST_HEAD, MarkTypeEnum.REG_IF_HEAD):
            tag_check_stack.append(tag)
        elif tag.type in (MarkTypeEnum.REG_IF_ELSEIF, MarkTypeEnum.REG_IF_ELSE):  # 注意:这两种元素不用入栈
            if len(tag_check_stack) < 1 or (tag_check_stack[len(tag_check_stack) - 1].type != MarkTypeEnum.REG_IF_HEAD):
                raise Exception("elseif或者else之前必须要有if标签.")
        elif tag.type == MarkTypeEnum.REG_IF_TAIL:
            if len(tag_check_stack) > 0 and tag_check_stack[len(tag_check_stack) - 1].type == MarkTypeEnum.REG_IF_HEAD:
                tag_check_stack.pop()
            else:
                raise Exception("</#if>标签之前必须有对应的if标签")
        elif tag.type == MarkTypeEnum.REG_LIST_TAIL:
            if len(tag_check_stack) > 0 and \
                    tag_check_stack[len(tag_check_stack) - 1].type == MarkTypeEnum.REG_LIST_HEAD:
                tag_check_stack.pop()
            else:
                raise Exception("</#list>标签之前必须有对应的list标签")
    # 所有元素出栈,说明标签匹配正确.
    if len(tag_check_stack) > 0:
        raise Exception("标签不配对")
